package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"zinx/src/utils"
	"zinx/src/ziface"
)

type Connection struct {
	//当前Conn属于哪个Server
	TcpServer  ziface.IServer
	Conn       *net.TCPConn
	ConnID     uint32
	isClosed   bool
	MsgHandler ziface.IMsgHandle //消息管理者
	//告知该连接已经退出/停止的channel
	ExitBuffChan chan bool
	//无缓冲管道，用于读写的两个协程之间的消息通信
	msgChan     chan []byte
	msgBuffChan chan []byte
	//连接属性
	property map[string]interface{}
	//保护连接属性修改的读写锁
	propertyLock sync.RWMutex
}

func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock() //加写锁
	defer c.propertyLock.Unlock()
	c.property[key] = value
}

func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}

func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)
}

// NewConnection 创建一个连接的结构体的函数
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32,
	msgHandler ziface.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer:    server,
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		MsgHandler:   msgHandler,         //记得要把路由也注册到connection中
		ExitBuffChan: make(chan bool, 1), //channel的缓冲区大小为1
		msgChan:      make(chan []byte),
		msgBuffChan:  make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		property:     make(map[string]interface{}),
	}
	//将新创建的conn添加到连接管理中
	c.TcpServer.GetConnMgr().Add(c) //避免TcpServer空指针
	return c
}

// StartWriter 写消息的协程，用户将数据发送到客户端
func (c *Connection) StartWriter() {
	fmt.Println("[write goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn writer exit...]")
	for {
		select {
		case data, _ := <-c.msgChan:
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("send data error", err)
				return
			}
		case <-c.ExitBuffChan:
			//如果从其中能够取出true,说明conn已经关闭
			return
		case data, ok := <-c.msgBuffChan:
			if ok {
				if _, err := c.Conn.Write(data); err != nil {
					fmt.Println("Send Buff Data error:, ", err, " Conn Writer exit")
					return
				}
			} else {
				break
			}
		}
	}
}

func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("connection closed when send buff msg")
	}
	//将data封包，并且发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id = ", msgId)
		return errors.New("Pack error msg ")
	}

	//写回客户端
	c.msgBuffChan <- msg

	return nil
}

// SendMsg 直接从服务端将msg数据发送数据给远程的tcp客户端
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("connection closed when send msg")
	}
	//将data封包
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id =", msgId)
		return errors.New("pack error msg")
	}
	//将封包的msg发送给channel给writer读取
	c.msgChan <- msg
	return nil
}

//StartReader 处理conn读数据的协程
//添加：在Connection调用注册的Router处理业务
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running...")
	defer fmt.Println(c.RemoteAddr().String(), "conn reader exit...")
	defer c.Stop()
	for {
		//创建拆包解包的对象
		dp := NewDataPack()
		//读取客户端的msg head
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error,", err)
			c.ExitBuffChan <- true //通知其他goroutine退出
			continue
		}
		//拆包，得到msgID和DataLen放在msg中
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error,", err)
			c.ExitBuffChan <- true
			continue
		}
		//根据dataLen读取data到msg.data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data := make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data error", err)
				c.ExitBuffChan <- true
				continue
			}
		}
		msg.SetData(data)
		//根据上述msg结果得到当前客户端请求的Request数据,进行实例化
		req := Request{
			conn: c,
			msg:  msg,
		}
		if utils.GlobalObject.WorkerPoolSize > 0 {
			//如果配置了工作池机制，就把任务交给他们去处理
			c.MsgHandler.SendMsgToTaskQueue(&req)
		} else {
			go c.MsgHandler.DoMsgHandler(&req)
		}
	}
}

func (c *Connection) Start() {
	//开启读写协程
	go c.StartReader()
	go c.StartWriter()
	//执行钩子方法
	c.TcpServer.CallOnConnStart(c)
}

func (c *Connection) Stop() {
	fmt.Printf("conn stop...connID = %d\n", c.ConnID)
	if c.isClosed == true {
		return
	}
	c.isClosed = true
	//执行钩子方法
	c.TcpServer.CallOnConnStop(c)
	//关闭连接
	err := c.Conn.Close()
	if err != nil {
		fmt.Println("Conn close error", err)
	}
	//通知从缓冲队列读取数据的业务，该连接已经被关闭了
	c.ExitBuffChan <- true
	//将该连接从连接管理器中删除
	c.TcpServer.GetConnMgr().Remove(c)
	//通过connection调用连接管理器
	close(c.ExitBuffChan) //关闭所有的通道
	close(c.msgBuffChan)
}

func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}
